package com.dji.GSDemo.GaodeMap;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.SurfaceTexture;
import android.os.Bundle;
import android.text.InputType;
import android.util.DisplayMetrics;
import android.util.Pair;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;

import com.dji.GSDemo.GaodeMap.Network.MonitorMessageListener;
import com.dji.GSDemo.GaodeMap.Network.MonitorNetConnection;
import com.dji.GSDemo.GaodeMap.Network.HttpVisitor;
import com.dji.GSDemo.GaodeMap.SDKInterconnection.SDKConnectionListener;
import com.dji.GSDemo.GaodeMap.UploadPicture.UAVPictureUploadListener;
import com.dji.GSDemo.GaodeMap.UploadPicture.UAVUploadPicture;
import com.dji.GSDemo.GaodeMap.utils.LatLonTool;
import com.dji.GSDemo.GaodeMap.SDKInterconnection.SDKConnection;
import com.dji.GSDemo.GaodeMap.utils.MyLog;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import dji.common.camera.SettingsDefinitions;
import dji.common.error.DJIError;
import dji.common.gimbal.Rotation;
import dji.common.gimbal.RotationMode;
import dji.common.mission.waypoint.Waypoint;
import dji.common.mission.waypoint.WaypointMissionDownloadEvent;
import dji.common.mission.waypoint.WaypointMissionExecutionEvent;
import dji.common.mission.waypoint.WaypointMissionFinishedAction;
import dji.common.mission.waypoint.WaypointMissionHeadingMode;
import dji.common.mission.waypoint.WaypointMissionUploadEvent;
import dji.common.useraccount.UserAccountState;
import dji.common.util.CommonCallbacks;

import dji.sdk.base.BaseProduct;
import dji.sdk.camera.Camera;
import dji.sdk.camera.VideoFeeder;
import dji.sdk.codec.DJICodecManager;
import dji.sdk.flightcontroller.FlightController;
import dji.sdk.gimbal.Gimbal;
import dji.sdk.products.Aircraft;
import dji.sdk.sdkmanager.DJISDKManager;
import dji.sdk.useraccount.UserAccountManager;

import static dji.sdk.sdkmanager.LiveVideoResolution.VIDEO_RESOLUTION_1280_960;

public class MyWaypointActivity extends FragmentActivity implements View.OnClickListener {

    private final int SchoolSafeRadius = 750;
    private final int SchoolMaxRadius = 1600;

    protected BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            onProductConnectionChange();
        }
    };

    private VideoFeeder.VideoDataListener mVideoDataListener;
    // 编码译码器
    private DJICodecManager mCodecManager;
    // 相机模式
    private SettingsDefinitions.CameraMode mCameraMode;

    private MyLog.MyLogListener myLogListener = new MyLog.MyLogListener() {

        @Override
        public void onLog(String msg) {
            appendLog(msg);
        }

        @Override
        public void onStore(String fileContents) {

        }

        @Override
        public void onRead(String fileContents) {

        }
    };

    //飞行器状态监听
    private final FlightStateMonitor.FlightStateListener flightStateListener = new FlightStateMonitor.FlightStateListener() {
        @Override
        public void onFlightStateChanged(Double lat, Double lon, Double altitude) {
            updateFlightControllerStateInfo(lat, lon, altitude);
        }

        @Override
        public void onRemainingBatteryChanged(Integer remainingChargeInPercent) {
            updateBatteryStateInfo(remainingChargeInPercent);
        }
    };

    private final UAVPictureUploadListener pictureUploadListener = new UAVPictureUploadListener() {
        @Override
        public void onLog(String message) {
            appendLog(message);
        }

        @Override
        public void onDownloadComplete() {

        }
    };

    private final MonitorMessageListener connectionListener = new MonitorMessageListener() {
        @Override
        public void log(String message) {
            appendLog(message);
        }

        @Override
        public void onServerConnected(int CID) {
            appendLog("CID: " + CID);
        }

        @Override
        public void onSendMessage(String message) {
            appendLog(message);
        }

        @Override
        public void onServerMsgReceived(String message) {
            appendLog(message);
        }

        @Override
        public void onServerReachabilityChanged(boolean isServerReachable) {
            appendLog("server reachable: " + isServerReachable);
        }

        @Override
        public void onMsgExecWaypointTask(int step, List<Waypoint> waypointList) {
            wayPointMissionController.stopMission();
            wayPointMissionController.setMissionPara(10.0f, WaypointMissionFinishedAction.GO_HOME, WaypointMissionHeadingMode.AUTO);
            wayPointMissionController.setWaypoints(waypointList);
            if (!wayPointMissionController.configMission()) return;
            if (!wayPointMissionController.loadMission()) return;
            wayPointMissionController.uploadAndStartMission();
        }
    };

    //航点任务监听
    WayPointMissionController.WaypointMissionListener waypointMissionListener = new WayPointMissionController.WaypointMissionListener() {
        @Override
        public void onConfigMission(boolean success) {
            if (success) {
                appendLog("Set Waypoint attitude successfully");
            } else {
                showToast("No waypoint added");
            }
        }

        @Override
        public void onLoadMission(DJIError error) {
            if (error == null) {
                appendLog("loadWaypoint succeeded");
            } else {
                showToast("loadWaypoint failed: " + error.getDescription());
            }
        }

        @Override
        public void onUploadMission(DJIError error, boolean isRetrying) {
            if (error == null) {
                appendLog("Mission upload  successfully!");
            } else {
                showToast("Mission upload failed: " + error.getDescription() + (isRetrying ? ". Retrying..." : ""));
            }
        }

        @Override
        public void onStartMission(DJIError error) {
            showToast("Mission Start: " + (error == null ? "Successfully" : error.getDescription()));
        }

        @Override
        public void onStopMission(DJIError error) {
            appendLog("Mission Stop: " + (error == null ? "Successfully" : error.getDescription()));
        }

        @Override
        public void onDownloadUpdate(@NonNull WaypointMissionDownloadEvent waypointMissionDownloadEvent) {

        }

        @Override
        public void onUploadUpdate(@NonNull WaypointMissionUploadEvent waypointMissionUploadEvent) {

        }

        @Override
        public void onExecutionUpdate(@NonNull WaypointMissionExecutionEvent waypointMissionExecutionEvent) {

        }

        @Override
        public void onExecutionStart() {

        }

        @Override
        public void onWaypointTargetChanged(int target) {
            appendLog("Point" + target + " arrived");
        }

        @Override
        public void log(String msg) {
            appendLog(msg);
        }

        @Override
        public void onExecutionFinish(@Nullable DJIError error) {
            String msg = "Execution finished: " + (error == null ? "Success!" : error.getDescription());
            showToast(msg);
        }
    };

    private final SDKConnectionListener sdkConnectionListener = new SDKConnectionListener() {
        @Override
        public void onReceive(String message) {
            final String REGEX = "(N)([-+]?[0-9]*\\.?[0-9]+)(-E)([-+]?[0-9]*\\.?[0-9]+)";
            Pattern p = Pattern.compile(REGEX);
            Matcher m = p.matcher(message);
            if (m.find() && m.groupCount() >= 4) {
                double lat = Double.parseDouble(m.group(2));
                double lon = Double.parseDouble(m.group(4));
                monitorConnection.sendBDSMessageToMonitor(lat, lon);
            } else {
                appendLog("InValid BDS message: " + message);
            }
        }
    };


    private final FlightStateMonitor flightStateMonitor = new FlightStateMonitor(flightStateListener);
    private final WayPointMissionController wayPointMissionController = WayPointMissionController.getInstance();
    private final MonitorNetConnection monitorConnection = MonitorNetConnection.getInstance();
    private final SDKConnection sdkConnection = SDKConnection.getInstance();
    private final GimbalController gimbal = GimbalController.getInstance();
    private final UAVUploadPicture pictureUploader = UAVUploadPicture.getInstance();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_my_waypoint);

        IntentFilter filter = new IntentFilter();
        filter.addAction(DJIDemoApplication.FLAG_CONNECTION_CHANGE);
        registerReceiver(mReceiver, filter);

        initUI();
        onProductConnectionChange();

        wayPointMissionController.setListener(waypointMissionListener);

        monitorConnection.addListener(connectionListener);
        sdkConnection.addListener(sdkConnectionListener);
        new HttpVisitor(HttpVisitor.HttpAddressToGetServerIP, res -> {
            monitorConnection.setIP(res);
            pictureUploader.setIP(res);
        }).request();

        pictureUploader.addListener(pictureUploadListener);
        MyLog.getInstance().addListener(myLogListener);

        initVideoDataListener();
//        MyWaypointActivity.this.runOnUiThread(() -> _appendLog(MyLog.getInstance().read()));
    }

    @Override
    protected void onResume() {
        super.onResume();
        flightStateMonitor.initFlightController();
        sdkConnection.updateProduct();
        gimbal.updateProduct();
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        unregisterReceiver(mReceiver);

        wayPointMissionController.removeListener(waypointMissionListener);
        monitorConnection.removeListener(connectionListener);

        removeVideoDataListener();

        super.onDestroy();
    }

    private TextView tv_lat, tv_lng, tv_altitude, tv_battery;
    private TextView tv_log;
    private ScrollView sv_log;
    // FPV显示区域
    private TextureView mTextureViewFPV;
    // 图传显示相关对象
    // VideoFeed视频流数据监听器

    private void initUI() {
        Button button_ip = findViewById(R.id.button_ip);
        Button button_connect_server = findViewById(R.id.button_connect_server);
        Button button_1_3 = findViewById(R.id.button_1_3);
        Button button_stop_mission = findViewById(R.id.button_stop_mission);
        Button btn_change_camera_mode = findViewById(R.id.btn_change_camera_mode);
        Button mChangeCameraMode = findViewById(R.id.button_2_3);
        Button mStartLive = findViewById(R.id.btn_start_live);

        mTextureViewFPV = findViewById(R.id.texture_fpv);

        tv_lat = findViewById(R.id.lat);
        tv_lng = findViewById(R.id.lng);
        tv_altitude = findViewById(R.id.alt);
        tv_battery = findViewById(R.id.battery);

        tv_log = findViewById(R.id.log);
        sv_log = findViewById(R.id.log_scroll);

        button_ip.setOnClickListener(this);
        button_connect_server.setOnClickListener(this);
        button_1_3.setOnClickListener(this);
        button_stop_mission.setOnClickListener(this);
        btn_change_camera_mode.setOnClickListener(this);
        mChangeCameraMode.setOnClickListener(this);
        mStartLive.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button_ip: {
                handleIPButtonClick();
                break;
            }
            case R.id.button_connect_server: {
                monitorConnection.connectToServer();
                break;
            }
            case R.id.button_1_3: {
                EditText yawView = findViewById(R.id.edit_text_yaw);
                EditText pitchView = findViewById(R.id.edit_text_pitch);
                EditText rollView = findViewById(R.id.edit_text_roll);
                Integer yaw = Integer.parseInt(yawView.getText().toString());
                Integer pitch = Integer.parseInt(pitchView.getText().toString());
                Integer roll = Integer.parseInt(rollView.getText().toString());
                gimbal.rotateAbsoluteAngle(yaw, pitch, roll, djiError -> {
                    if (djiError != null) appendLog(djiError.getDescription());
                });
                break;
            }
            case R.id.button_stop_mission: {
                wayPointMissionController.stopMission();
                break;
            }
            case R.id.btn_start_live: {
                startLive();
                break;
            }
            case R.id.btn_change_camera_mode: {
                changeCameraMode();
                break;
            }
            case R.id.button_2_3: {
                MyLog.getInstance().log("1111");
                break;
            }
            default:
                break;
        }
    }

    // 设置IP
    private void handleIPButtonClick() {

        // IP设置文本框
        final EditText editText = new EditText(this);
        editText.setInputType(InputType.TYPE_CLASS_TEXT);  // 只能输入纯数字
        editText.setText(monitorConnection.getIpAddress());

        // 弹出设置IP对话框
        new AlertDialog.Builder(this)
                .setView(editText)
                .setPositiveButton("修改", (dialog, which) -> {
                    String ip = editText.getText().toString();
                    monitorConnection.setIP(ip);
                    pictureUploader.setIP(ip);
                })
                .setNegativeButton("退出", (dialog, which) -> {
                })
                .show();
    }

    private void initFlightController() {

        BaseProduct product = DJIDemoApplication.getProductInstance();
        if (product != null && product.isConnected()) {
            if (product instanceof Aircraft) {
                FlightController flightcontroller = ((Aircraft) product).getFlightController();
                flightcontroller.setMaxFlightRadius(SchoolSafeRadius,null);
                flightcontroller.setMaxFlightHeight(120,null);
            }
        }
    }

    private void onProductConnectionChange() {
        flightStateMonitor.initFlightController();
        sdkConnection.updateProduct();
        gimbal.updateProduct();
        initFlightController();
        loginAccount();
    }

    private void loginAccount() {
        UserAccountManager.getInstance().logIntoDJIUserAccount(this,
                new CommonCallbacks.CompletionCallbackWith<UserAccountState>() {
                    @Override
                    public void onSuccess(final UserAccountState userAccountState) {
                        appendLog("Login Success");
                    }

                    @Override
                    public void onFailure(DJIError error) {
                        showToast("Login Error:" + error.getDescription());
                    }
                });
    }

    private void updateFlightControllerStateInfo(Double lat, Double lon, Double altitude) {
        if (Double.isNaN(lat)) return;
        monitorConnection.setUAVInfo(lat, lon, altitude);
        MyWaypointActivity.this.runOnUiThread(
                new Runnable() {
                    @Override
                    public void run() {
                        tv_lat.setText(String.valueOf(lat));
                        tv_lng.setText(String.valueOf(lon));
                        tv_altitude.setText(String.valueOf(altitude));
                    }
                }
        );
    }

    private void updateBatteryStateInfo(Integer remainingChargeInPercent) {
        MyWaypointActivity.this.runOnUiThread(
                new Runnable() {
                    @Override
                    public void run() {
                        tv_battery.setText(String.valueOf(remainingChargeInPercent));
                    }
                }
        );
    }

    private void setFlyAroundWaypoint() {
        List<Waypoint> waypointList = new ArrayList<Waypoint>();
        int pointsNum = 8;
        double perAngle = 360.0 / pointsNum;
        double radius = 20;
        double lat = flightStateMonitor.getLat(), lon = flightStateMonitor.getLon();
        double altitude = flightStateMonitor.getAltitude();
        for (int i = 0; i < pointsNum; i++) {
            Pair<Double, Double> latLon = LatLonTool.GetOtherPoint(lat, lon, radius, perAngle * i);
            Waypoint mWaypoint = new Waypoint(latLon.first, latLon.second, (float) altitude);
            waypointList.add(mWaypoint);
        }
        wayPointMissionController.setWaypoints(waypointList);
    }

    // 初始化监听器
    private void initVideoDataListener() {
        // 为用于显示图传数据的TextureView设置监听器
        mTextureViewFPV.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                // 在SurfaceTexture可用时创建解码译码器
                if (mCodecManager == null) {
                    mCodecManager = new DJICodecManager(MyWaypointActivity.this, surface, width, height);
                    fitTextureViewToFPV();
                    mCodecManager.setOnVideoSizeChangedListener(new DJICodecManager.OnVideoSizeChangedListener() {
                        @Override
                        public void onVideoSizeChanged(int i, int i1) {
                            // fitTextureViewToFPV();
                        }
                    });
                }
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
                // fitTextureViewToFPV();
            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                // 在SurfaceTexture销毁时释放解码译码器
                if (mCodecManager != null) {
                    mCodecManager.cleanSurface();
                    mCodecManager = null;
                }
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });

        // 为VideoFeed设置视频流数据监听器
        mVideoDataListener = new VideoFeeder.VideoDataListener() {
            @Override
            public void onReceive(byte[] bytes, int i) {
                if (mCodecManager != null) {
                    mCodecManager.sendDataToDecoder(bytes, i);
                }
            }
        };

        // 为VideoFeed设置监听器对象，获取图传视频流
        VideoFeeder.getInstance().getPrimaryVideoFeed().addVideoDataListener(mVideoDataListener);

    }

    // 移除监听器
    private void removeVideoDataListener() {
        // 移除VideoFeed的视频流数据监听器
        VideoFeeder.getInstance().getPrimaryVideoFeed().removeVideoDataListener(mVideoDataListener);

        // 移除相机回调
        Camera camera = getCamera();
        if (camera != null) {
            camera.setSystemStateCallback(null);
        }

        // 移除云台回调
        Gimbal gimbal = getGimbal();
        if (gimbal != null) {
            gimbal.setStateCallback(null);
        }
    }

    // 使TextureView的宽高比适合视频流
    private void fitTextureViewToFPV() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // 用于获取屏幕高度和宽度的DisplayMetrics对象。
                DisplayMetrics dm = new DisplayMetrics();
                getWindowManager().getDefaultDisplay().getMetrics(dm);
                // 图传视频的宽高比
                double videoratio = mCodecManager.getVideoWidth() * 1.0 / mCodecManager.getVideoHeight();
                // 设备屏幕的宽高比
                double textureratio = dm.widthPixels * 1.0 / dm.heightPixels;
                if (videoratio == textureratio) {
                    // 无需调整，直接返回
                    return;
                }
                // 开始设置TextureView的宽度和高度
                ViewGroup.LayoutParams layoutParams = mTextureViewFPV.getLayoutParams();
                if (videoratio > textureratio) {
                    // 如果视频宽高比更大，则使TextureView的宽度占满屏幕，设置其高度满足图传的宽高比
                    layoutParams.height = (int) (dm.widthPixels / videoratio);
                    layoutParams.width = dm.widthPixels;
                }
                if (videoratio < textureratio) {
                    // 如果设备宽高比更大，则使TextureView的高度占满屏幕，设置其宽度满足图传的宽高比
                    layoutParams.height = dm.heightPixels;
                    layoutParams.width = (int) (dm.heightPixels * videoratio);
                }
                // 设置TextureView的宽度和高度
                mTextureViewFPV.setLayoutParams(layoutParams);
                // 通知编码译码器TextureView的宽度和高度的变化
                mCodecManager.onSurfaceSizeChanged(layoutParams.width, layoutParams.height, 0);

            }
        });
    }

    private void startLive() {
        new Thread() {
            @Override
            public void run() {
                int result = 0;
                String ipAddr = monitorConnection.getIpAddress();
                int CID = monitorConnection.getCID();
                try {
                    if (!isLiveStreamManagerOn()) {
                        showToast("Live Stream Manager is off.");
                        return;
                    }
                    if (DJISDKManager.getInstance().getLiveStreamManager().isStreaming()) {
                        //wang
                        showToast("Live Stream is already started!");
                        //            ToastUtils.setResultToToast("already started!");
                        DJISDKManager.getInstance().getLiveStreamManager().stopStream();
                        return;
                    }
                    DJISDKManager.getInstance().getLiveStreamManager().setLiveUrl("rtmp://" + ipAddr + ":1935/live/" + Integer.toString(CID));
                    showToast("have set liveurl");
                    long sleeptime = 5000;
                    Thread.sleep(sleeptime);
                    DJISDKManager.getInstance().getLiveStreamManager().setVideoEncodingEnabled(true);
                    DJISDKManager.getInstance().getLiveStreamManager().setLiveVideoResolution(VIDEO_RESOLUTION_1280_960);
                    result = DJISDKManager.getInstance().getLiveStreamManager().startStream();
                    showToast("have started livestream");
                    Thread.sleep(sleeptime);
                    showToast("start live thread is running. result is " + Integer.toString(result));
                    showToast("rtmp://" + ipAddr + ":1935/live/" + Integer.toString(CID));
                } catch (Exception ex) {
                    showToast("start live thread failed. result is " + Integer.toString(result));
                    showToast("rtmp://" + ipAddr + ":1935/live/" + Integer.toString(CID));
                }
            }
        }.start();
    }

    private boolean isLiveStreamManagerOn() {
        if (DJISDKManager.getInstance().getLiveStreamManager() == null) {
            Toast.makeText(getApplicationContext(), "No live stream manager!", Toast.LENGTH_SHORT).show();
            return false;
        }
        return true;
    }

    // 改变相机模式
    private void changeCameraMode() {
        Camera camera = getCamera();
        if (camera != null) {
            if (mCameraMode == SettingsDefinitions.CameraMode.RECORD_VIDEO) {
                // 如果处在拍照模式，则进入录像模式
                setCameraMode(camera, SettingsDefinitions.CameraMode.SHOOT_PHOTO);
            } else {
                // 如果处在非拍照模式，则进入拍照模式
                setCameraMode(camera, SettingsDefinitions.CameraMode.RECORD_VIDEO);
            }
        }
    }

    // 设置指定的相机模式
    private void setCameraMode(Camera camera, final SettingsDefinitions.CameraMode cameraMode) {

        camera.setMode(cameraMode, new CommonCallbacks.CompletionCallback() {
            @Override
            public void onResult(DJIError djiError) {
                if (djiError == null) {
                    showToast("相机模式设置成功:" + cameraModeToString(cameraMode));
                } else {
                    showToast("相机模式设置失败:" + djiError.getDescription());
                }
            }
        });
    }

    // 相机模式枚举值转字符串
    private String cameraModeToString(SettingsDefinitions.CameraMode cameraMode) {
        switch (cameraMode) {
            case SHOOT_PHOTO:
                return "SHOOT_PHOTO 拍照模式";
            case RECORD_VIDEO:
                return "RECORD_VIDEO 录像模式";
            case PLAYBACK:
                return "PLAYBACK 回放模式";
            case MEDIA_DOWNLOAD:
                return "MEDIA_DOWNLOAD 媒体下载模式";
            case BROADCAST:
                return "BROADCAST 直播模式";
            case UNKNOWN:
                return "UNKNOWN 未知模式";
            default:
                return "N/A";
        }
    }

    // 获得无人机（或手持云台相机）的相机对象
    private Camera getCamera() {
        BaseProduct product = DJISDKManager.getInstance().getProduct();
        if (product != null && product.isConnected()) {
            return product.getCamera();
        }
        return null;
    }

    // 获得无人机（或手持云台相机）的云台对象
    private Gimbal getGimbal() {
        BaseProduct product = DJISDKManager.getInstance().getProduct();
        if (product != null && product.isConnected()) {
            return product.getGimbal();
        }
        return null;
    }

    private void showToast(String msg) {
        showToast(msg, Toast.LENGTH_SHORT);
    }

    private void showToast(String msg, int len) {
        MyWaypointActivity.this.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(MyWaypointActivity.this, msg, len).show();
                appendLog(msg);
            }
        });
    }

    private void appendLog(String msg) {
        Date d = new Date();
        SimpleDateFormat df = new SimpleDateFormat("HH:mm");
        msg = df.format(d) + "  " + msg + '\n';
        String finalMsg = msg;
//        MyLog.getInstance().store(finalMsg);
        MyWaypointActivity.this.runOnUiThread(() -> _appendLog(finalMsg));
    }

    private void _appendLog(String msg) {
        tv_log.append(msg);
//        scrollToBottom(sv_log, tv_log);
    }

    private void scrollToBottom(final ScrollView scrollView, final View view) {
        if (scrollView == null || view == null) {
            return;
        }

        int offset = view.getMeasuredHeight()
                - scrollView.getMeasuredHeight();
        if (offset < 0) {
            offset = 0;
        }

        scrollView.scrollTo(0, offset);
    }
}
